#include <stdlib.h>
#include <stdio.h>

#include "TDA_Lista.h"


#define LISTA_FIN NULL

#define TRUE 1
#define FALSE 0

struct Nodo {
  tDato dato;
  struct Nodo* sig;
};

typedef struct Nodo* tNodo;

struct Lista {
  tNodo raiz;
  tFcnDestructorDato fcnDestructorDato;
};

struct IteradorLista {
  tLista lista;
  tNodo cursor;
  int enInicio;
};

//==============================================================================

tNodo listaRemoverNodo (tLista pLista, tNodo pNodo);

tNodo crearNodo (tDato pDato);

void listaDestruirNodo (tNodo pNodo);

//==============================================================================

void listaCrear (tLista* pLista, tFcnDestructorDato pFcnDestructorDato){

  if (pLista && pFcnDestructorDato)
  {
    tLista lista = malloc (sizeof (struct Lista));
    
    if (lista)
    {
      lista->raiz = LISTA_FIN;
      lista->fcnDestructorDato = pFcnDestructorDato; 
    }  
    *pLista = lista;
  }
}

//------------------------------------------------------------------------------

void listaDestruir (tLista pLista){

  while (!listaVacia(pLista))
  {
    tNodo temp = listaRemoverNodo (pLista, pLista->raiz);
    if (NULL != temp) 
    {
      pLista->fcnDestructorDato(temp->dato);
      listaDestruirNodo (temp);
    }
  }
}

//------------------------------------------------------------------------------

int listaAgregarFinal (tLista pLista, tDato pDato){
  
  int agregado = FALSE;
    
  if (pLista && pDato)
  {
    tNodo nodo =  crearNodo (pDato);
    if (nodo)
    {
      agregado = TRUE;      
      if (listaVacia (pLista))
        pLista->raiz = nodo;
      else
      {
        //Busco el ultimo nodo de la lista.
        tNodo cursor = pLista->raiz;
        
        while (LISTA_FIN != cursor->sig)
            cursor = cursor->sig;
        
        cursor->sig = nodo;
      }
    }
  }  
  return agregado;
}

//------------------------------------------------------------------------------

tDato listaRemover (tLista pLista, tDato pDato)
{
  if (!listaVacia (pLista))
  {
    // Busco el nodo que contiene al dato.
    tNodo cursor = pLista->raiz;
    while (LISTA_FIN != cursor && cursor->dato != pDato)
      cursor = cursor->sig;
    
    if (LISTA_FIN != cursor)
    {
      // Encontre al nodo que contiene al dato.
      cursor = listaRemoverNodo(pLista, cursor);
      if (cursor)
      {
        pLista->fcnDestructorDato(cursor->dato);
        listaDestruirNodo(cursor);
      }
    }
    else
      // El dato no esta en la lista.
      pDato = NULL;
  }
  else
    // El dato no esta en la lista.
    pDato = NULL;
  
  return pDato;
}

//------------------------------------------------------------------------------

int listaVacia (tLista pLista){

  int vacia = TRUE;

  if (pLista)
    vacia = pLista->raiz == LISTA_FIN;

  return vacia;
}

//------------------------------------------------------------------------------

tIteradorLista listaObtenerIterador (tLista pLista){

  tIteradorLista iterador = NULL;

  if (pLista)
  {
    iterador = malloc (sizeof (struct IteradorLista));
    if (iterador)
    {
      iterador->lista = pLista;
      iterador->cursor = NULL;
      iterador->enInicio = TRUE;
    }
  } 
  return iterador;
}

//------------------------------------------------------------------------------

void listaLiberarIterador (tIteradorLista pIterador){

  if (pIterador)
  {
    pIterador->lista = NULL;
    pIterador->cursor = NULL;
    free (pIterador);
  }
}

//------------------------------------------------------------------------------

int listaIteradorMoverSiguiente (tIteradorLista pIterador){
  
  int exito = FALSE;
  
  if (pIterador)  
    if (!listaVacia (pIterador->lista))
    {
      if (TRUE == pIterador->enInicio)
      {
        pIterador->cursor = pIterador->lista->raiz;
        pIterador->enInicio = FALSE;
      }
      else if (LISTA_FIN != pIterador->cursor)
        pIterador->cursor = pIterador->cursor->sig;
        
      exito = pIterador->cursor != LISTA_FIN;

    }
  return exito;
}

//------------------------------------------------------------------------------

int listaIteradorMoverPrimero (tIteradorLista pIterador){
  
  if (pIterador)
  {
    pIterador->enInicio = TRUE;
    pIterador->cursor = NULL;
  }
}

//------------------------------------------------------------------------------

tDato listaIteradorActual(tIteradorLista pIterador){
  
  tDato val = NULL;
  
  if (pIterador)
  {
    if (LISTA_FIN != pIterador->cursor)
      val = pIterador->cursor->dato;    
  }  
  return val;
}

//==============================================================================

tNodo listaRemoverNodo (tLista pLista, tNodo pNodo)
{

  if (pLista && pNodo)
  {
    tNodo cursor = pLista->raiz
        , previo = NULL;  
   
    while (cursor != pNodo && LISTA_FIN != cursor)
    {
      if (cursor != pNodo)
      {
        previo = cursor;
        cursor = cursor->sig;
      }
    }
    if (cursor == pNodo)
    {
      if (NULL == previo)
      {
        pLista->raiz = pNodo->sig;
      }
      else
      {
        previo->sig = pNodo->sig;
      }
    }
    else
      pNodo = NULL;
  }
  else
    pNodo = NULL;
    
  return pNodo;
}

//------------------------------------------------------------------------------

void listaDestruirNodo (tNodo pNodo)
{
  if (pNodo)
    free (pNodo);
}

//------------------------------------------------------------------------------

tNodo crearNodo (tDato pDato)
{
  tNodo nodo = NULL;
  
  if (pDato)
  {
    nodo = malloc (sizeof (struct Nodo));
    if (nodo)
    {
      nodo->dato = pDato;
      nodo->sig = LISTA_FIN;
    }
  }
  return nodo;
}
